/*
	Copyright 2008, 2009, 2010 Brightworks, Inc.
	
	This file is part of Language Mentor.
	
	Language Mentor is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	
	Language Mentor is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License
	along with Language Mentor.  If not, see <http://www.gnu.org/licenses/>.
*/
package com.langcollab.languagementor.controller.audio
{
    import com.brightworks.util.Debug;
    import com.langcollab.languagementor.constant.Constant_Misc;
    
    import flash.events.ErrorEvent;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SampleDataEvent;
    import flash.events.SecurityErrorEvent;
    import flash.events.TimerEvent;
    import flash.filesystem.File;
    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.media.SoundLoaderContext;
    import flash.media.SoundMixer;
    import flash.net.URLRequest;
    import flash.utils.ByteArray;
    import flash.utils.Timer;

    [Event(name="complete",       type="flash.events.Event"              )]
    [Event(name="id3",            type="flash.events.Event"              )]
    [Event(name="ioError",        type="flash.events.IOErrorEvent"       )]
    [Event(name="open",           type="flash.events.Event"              )]
    [Event(name="progress",       type="flash.events.ProgressEvent"      )]
    [Event(name="securityError",  type="flash.events.SecurityErrorEvent" )]
    [Event(name="soundComplete",  type="flash.events.Event"              )]

    public class AudioPlayer extends EventDispatcher
    {
        public static const PLAY_PROGRESS:String = "playProgress";

        private static var _audioPlayer:com.langcollab.languagementor.controller.audio.AudioPlayer;

        private var _bufferTime:int = 1000;
		private var _isPlaying:Boolean;
		private var _outputSound:Sound;
		private var _rawSoundDataUnadjustedMaxVolume:Number;
		private var _sourceSound:Sound;
		private var _soundVolumeAdjustmentFactor:Number;

        public static function getInstance():com.langcollab.languagementor.controller.audio.AudioPlayer
        {
            if ( _audioPlayer == null )
            {
               _audioPlayer = new com.langcollab.languagementor.controller.audio.AudioPlayer();
            }
            return _audioPlayer;
        }

        public function AudioPlayer()
        {
            if ( _audioPlayer != null )
            {
				Debug.error_Fatal("Don't instantiate AudioPlayer with new() - use getInstance()");
            }
        }

        // ****************************************************
        //
        //          Public Methods
        //
        // ****************************************************

		public function play(soundUrl:String, rawSoundDataUnadjustedMaxVolume:Number):void
        {
            if (_isPlaying)
            {
                // 2008.05.12  I've hit this error once but have been unable to reproduce it...
				Debug.warning("AudioPlayer.play(): called while sound is playing");
            }
			if (rawSoundDataUnadjustedMaxVolume < .01)
			{
				// We have a serious problem - this is probably the module creator's fault.
				// Play audio that says "faulty audio" or some such? Or, better, catch in 
				// download process. ///
				Debug.warning("AudioPlayer.play(): rawSoundDataUnadjustedMaxVolume < .01");
				return;
			}
            clear();
			_rawSoundDataUnadjustedMaxVolume = rawSoundDataUnadjustedMaxVolume;
			_soundVolumeAdjustmentFactor = (Constant_Misc.AUDIO_MAX_VOLUME / rawSoundDataUnadjustedMaxVolume);
            _sourceSound = new Sound();
            _sourceSound.addEventListener(Event.COMPLETE, onSourceSoundLoadComplete);
            _sourceSound.addEventListener(IOErrorEvent.IO_ERROR, onSourceSoundLoadError);
            _sourceSound.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSourceSoundLoadError);
			_outputSound = new Sound();
			_outputSound.addEventListener(SampleDataEvent.SAMPLE_DATA, onOutputSoundSampleData);
			// dmccarroll - 20110120
			// Must use a File object to get property formatted url arg for URLRequest.
			// This is only necessary on Android, not on Windows, but is the 'correct' 
			// way to do this.
			var f:File = new File(soundUrl);
            var req:URLRequest = new URLRequest(f.url);
            var context:SoundLoaderContext = new SoundLoaderContext(_bufferTime, true);
            _sourceSound.load(req, context);
        }

        public function stop(pos:int = 0):void
        {
			clear();
        }

        // ****************************************************
        //
        //          Private Methods
        //
        // ****************************************************

		private function clear():void
		{
			if (_outputSound)
			{
				_outputSound.removeEventListener(SampleDataEvent.SAMPLE_DATA, onOutputSoundSampleData);
    			_outputSound = null;
			}
			if (_sourceSound)
			{
				_sourceSound.removeEventListener(Event.COMPLETE, onSourceSoundLoadComplete);
				_sourceSound.removeEventListener(IOErrorEvent.IO_ERROR, onSourceSoundLoadError);
				_sourceSound.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onSourceSoundLoadError);
    			_sourceSound = null;
			}
			_isPlaying = false;
			_rawSoundDataUnadjustedMaxVolume = 0;
			_soundVolumeAdjustmentFactor = 0;
		}
		
		private function onOutputSoundSampleData(event:SampleDataEvent):void
		{
			var sourceBytes:ByteArray = new ByteArray();
			var modifiedBytes:ByteArray = new ByteArray();
			_sourceSound.extract(sourceBytes, 4096);
			if (sourceBytes.length < (8 * 4096))
				var isLastSample:Boolean = true;
			sourceBytes.position = 0;
			while(sourceBytes.bytesAvailable > 0)
			{
				modifiedBytes.writeFloat(sourceBytes.readFloat() * _soundVolumeAdjustmentFactor);
			}
			event.data.writeBytes(modifiedBytes);
			if (isLastSample)
			{
				clear();
				dispatchEvent(new Event(Event.SOUND_COMPLETE));
			}
		}
		
        private function onSourceSoundLoadComplete(event:Event):void
        {
			_outputSound.play();
        }

		private function onSourceSoundLoadError(event:ErrorEvent):void
		{
			clear();
			dispatchEvent(event.clone());
		}
		
    }
}


















